#include "common.h"
#include <time.h>

#include "lgc.h"
#include "lauxlib.h"
#include "lualib.h"
#include "common.h"

#include <winsock2.h>
#pragma  comment(lib,"ws2_32.lib")

#define MAX_BUF 2048 // 缓存最大值
#define PROGRAM "janbar"

/**
* 连接服务器,得到socket对象
**/
int cli_connect(const char *ip,const unsigned short port)
{
  WSADATA data; // Winsock服务初始化
  if(WSAStartup(MAKEWORD(2,2), &data) != 0) {
    printf("WSAStartup error\n");
    return -1;
  }

  int cli = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (cli == INVALID_SOCKET) {
    printf("socket error\n");
    return -1;
  }

  struct sockaddr_in addr = {
    .sin_family      =  PF_INET,
    .sin_port        =  htons(port),
    .sin_addr.s_addr =  inet_addr(ip),
  };
  memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
  if (connect(cli, (struct sockaddr*)&addr, sizeof(struct sockaddr)) == SOCKET_ERROR) {
    printf("connect error\n");
    return -1;
  }
  return cli;
}

typedef struct {
  char flag[4];
  unsigned int len, crc;
}LuaCodeHead;
// 校验得到的lua字节码,正确则返回lua字节码长度
unsigned int check_lua_code(const char *buffer,const unsigned int size)
{
  if (size < 12) { // 连头部12字节都没有
    printf("data is not enough length\n");
    return -1;
  }
  LuaCodeHead *head = (LuaCodeHead *)buffer;
  if (size != head->len+12) {
    printf("data len error\n");
    return -1;
  }
  if (strncmp("luac", head->flag, 4) != 0) {
    printf("not have luac flag\n");
    return -1;
  }
  if (head->crc != crc32(buffer+12,head->len)) {
    printf("check crc32 error\n");
    return -1;
  }
  return head->len;
}

// 获取系统时间
static int get_now_time(lua_State *L) {
  luaC_fullgc(L,0);
  time_t timep = time(NULL);
  struct tm *p = gmtime(&timep);
  lua_pushinteger(L, 1900+p->tm_year);/*获取当前年份,从1900开始，所以要加1900*/
  lua_pushinteger(L, 1+p->tm_mon);/*获取当前月份,范围是0-11,所以要加1*/
  lua_pushinteger(L, p->tm_mday);/*获取当前月份日数,范围是1-31*/
  lua_pushinteger(L, 8+p->tm_hour);/*获取当前时,这里获取西方的时间,刚好相差八个小时*/
  lua_pushinteger(L, p->tm_min);/*获取当前分*/
  lua_pushinteger(L, p->tm_sec);/*获取当前秒*/
  lua_pushinteger(L, p->tm_yday);/*从今年1月1日算起至今的天数，范围为0-365*/
  return 7;
}

static const luaL_Reg janbar_lib[] = {
  {"get_now_time", get_now_time},
  {NULL, NULL}
};

LUALIB_API int luaopen_janbar (lua_State *L) {
  luaL_newlib(L, janbar_lib);
  return 1;
}

int main(int argc, char *argv[])
{
  int cli = cli_connect("127.0.0.1",3456);
  if (cli < 0)
    return -1;

  lua_State* L = luaL_newstate();
  if (NULL == L) {
    return -1;
  }
  lua_gc(L, LUA_GCSTOP, 0);  //关闭gc
  luaL_openlibs(L);
  luaL_requiref(L,"janbar",luaopen_janbar,0);
  lua_gc(L, LUA_GCRESTART, 0);//执行gc 

  int status, len;
  char buffer[MAX_BUF];
  while(1) {
		if ( 0 >= (len = recv(cli, buffer, MAX_BUF, 0)) ) {
			break;
		}

    len = check_lua_code(buffer, len);
    if (len > 0) {
      printf("run lua code,len = %d\n", len);
      status = luaL_loadbuffer(L,buffer+12,len,PROGRAM); // 加载lua字节码
      if(status == 0) {
        status = lua_pcall(L, 0, LUA_MULTRET, 0);        // 启动lua虚拟机
        if (status != 0) {                               // 失败则打印信息并退出
          lua_gc(L, LUA_GCCOLLECT, 0);
          if (!lua_isnil(L, -1)) {
            printf("%s\n", lua_tostring(L,-1));
            lua_pop(L, 1);
          }
          return -1;
        } else { // c调用lua代码
          lua_getglobal(L, "get_sec");
          if (!lua_isfunction(L, -1)) {
            printf("get_sec function failed\n");
            lua_pop(L, 1);
            return -1;
          }
          status = lua_pcall(L, 0, 7, 0);
          for (len=0;len<7;len++){
             if ( lua_isnumber( L, -1 ) ){
                printf("%d,",lua_tointeger( L, -1 ));
                lua_pop(L, 1);
             }
          }
          printf("\n");
        }
      } else {
        printf("%s\n", lua_tostring(L,-1));
      }
    }
    // 发信息给服务器,然后重新获取字节码
    fgets(buffer, MAX_BUF, stdin);
    if (buffer[0] == '\n') {
      strcpy(buffer,"0k");
    } else {
      *strchr(buffer, '\n') = '\0';
    }
    send(cli,buffer,strlen(buffer),0);
  }

  lua_close(L);
  closesocket(cli);
  WSACleanup(); // 终止Winsock服务
  return 0;
}